package logica_de_negocio;

import java.util.ArrayList;
import java.util.HashSet;

import logica_de_datos.GestorBD;
import comun.Pelicula;
import comun.ValorId_p;

/**
 * Clase encargada de la gestion de las peliculas. Ademas tiene una conexion con
 * la base de datos lo cual le permite ser persistente.
 * 
 * 
 */
public class gestorPeli {

	private HashSet<Pelicula> listaPeliculas;
	final GestorBD objConversor = GestorBD.getInstance();

	/**
	 * Inicializacion del HashSet en el que se encuentra las peliculas.
	 */
	public gestorPeli() {
		listaPeliculas = new HashSet<Pelicula>();
	}

	/**
	 * Introduciomos todas las peliculas que hay en la base de datos a un
	 * HashSet.
	 * 
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public void cargarDatos() throws ClassNotFoundException {
		listaPeliculas = null;

		objConversor.connect();
		listaPeliculas = objConversor.listarPeliculas();
		objConversor.disconnect();

	}

	/**
	 * Anadimos la pelicula que nos han pasado por parametro y le introducimos
	 * un id. A su vez se verifica que no exista ya una pelicula con ese titulo
	 * y de ese director en la base de datos. Si esto ultimo llegase a ocurrir
	 * se lanzaria una excepcion.
	 * 
	 * @param peli
	 *            ->Pelicula que se desea introducir en la base de datos
	 * @throws excepAnadirDupli
	 *             Si se produce que la existe
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public void anadirPeli(Pelicula peli) throws excepAnadirDupli,
			ClassNotFoundException {

		cargarDatos();
		if (listaPeliculas.contains(peli)) {// ///REVISAR
			excepAnadirDupli dupli = new excepAnadirDupli();
			dupli.setDes("La pelicula con titulo " + peli.getTitulo()
					+ " y director " + peli.getDirector()
					+ " ya esta en la base de datos");
			throw dupli;
		} else {

			objConversor.connect();
			objConversor.addPelicula(peli);
			objConversor.disconnect();

		}
	}

	/**
	 * Modifica la pelicula que se pasa por con los valores
	 * deseamos.Primeramente se verifica que la pelicula sigue existiendo en la
	 * base de datos de no ser asi lanza una excepcion. Ademas, debido a que la
	 * relacion entre titulo y director debe ser unica se revisa antes.
	 * 
	 * @see #regla(Pelicula p)
	 * @param p
	 *            Pelicula como Objeto que se desea modificar
	 * @return Devuelve un true si se ha conseguido moficiar y un false si no
	 *         cumple las reglas.
	 * @throws FaltanDatosEnLaBaseDeDatos
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public boolean modificarPeli(Pelicula p) throws FaltanDatosEnLaBaseDeDatos,
			ClassNotFoundException {

		cargarDatos();
		boolean dev = false;

		if (isId(p.getId_p())) {
			if (regla(p)) {// LA RELACION titulo director debe ser unica

				objConversor.connect();
				objConversor.modifyPelicula(p);
				dev = true;
				objConversor.disconnect();

			}

		} else {
			FaltanDatosEnLaBaseDeDatos f = new FaltanDatosEnLaBaseDeDatos();
			f.setFalta("No existe la pelicula con el id-" + p.getId_p());
			throw f;
		}
		return dev;
	}

	/**
	 * Metodo privado que verifica,cuando se esta modificando una pelicula, que
	 * la pelicula siga cumpliendo que la relacion entre titulo y director sea
	 * unica. Ademas, si no la pelicula ya no esta en la base de datos lanza una
	 * excepcion.
	 * 
	 * @param p
	 *            ->Pelicula (Obj) la cual tiene que cumplir la regla
	 * @return Devuelve un true si la pelicula pasada cumple que la regla de
	 *         relacion unica entre titulo y director.False cuando no se cumpla.
	 * @throws FaltanDatosEnLaBaseDeDatos
	 *             Se lanza cuando la pelicula ya no exista en la base de datos.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	private boolean regla(Pelicula p) throws FaltanDatosEnLaBaseDeDatos,
			ClassNotFoundException {
		boolean dev = true;
		Pelicula pel = getPelicula(p.getId_p());// LOS CON EL MISMO ID
		if (pel.equals(p)) {// UNICAMENTE SE MODIFICAN ASPECTOS NO IMPORTANTES
			dev = true;
		} else {// REVISAR SI HAY ALGUNA PELICULA QUE YA TENGA ESAS
				// CARCATERISTCAS PERO CON OTRO ID
			for (Pelicula peli : listaPeliculas) {
				if (peli.getTitulo().equals(p.getTitulo())) {
					if (peli.getDirector().equals(p.getDirector())) {
						if (peli.getId_p() != p.getId_p()) {
							dev = false;// YA EXISTE UNO
						}
					}
				}

			}
		}
		return dev;

	}

	/**
	 * Metodo utilizado para la eliminacion de una pelicula mediante su id,
	 * teniendo en cuenta las posibles relaciones con los usuarios y las
	 * valoraciones que puedan existir de las mismas. Si existen se revisara que
	 * el usuario atienda si desea un borrado en cascada para los cual se
	 * lanzara una excepcion si esta no tiene en su interior un Aceptado=true,
	 * en cuyo caso se sabra que el usuario desea proseguir con ello.
	 * 
	 * @param id_p
	 *            -> Id de la pelicula a eliminar
	 * @param exceDOnC
	 *            -> Excepcion que se utilizada como verificardor que el usuario
	 *            desea borrar
	 * @return True si la pelicula se elimina con exito. False si no se consigue
	 *         borrar.
	 * @throws ExcepDeleteOnCascade
	 *             Cuando se necesita la intermediacion del usuario para
	 *             verificar el borrado en cascada.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public boolean borrar(int id_p, ExcepDeleteOnCascade exceDOnC)
			throws ExcepDeleteOnCascade, ClassNotFoundException {
		cargarDatos();
		boolean rea = false;

		gestorPeliculaVistaNoSi listaPeliculasns = new gestorPeliculaVistaNoSi();
		gestorValoracionPelicula gvp = new gestorValoracionPelicula();

		boolean permitido = exceDOnC.isAceptado();

		if (permitido == false) {// La segunda vuelta
			if ((gvp.isId_p(id_p)) || (listaPeliculasns.isId_p(id_p))) {
				ExcepDeleteOnCascade edoc = new ExcepDeleteOnCascade();
				edoc.setAceptado(false);
				throw edoc;
			} else {
				permitido = true;
			}
		}
		if (permitido == true) {
			boolean relPNS = false;
			if (listaPeliculasns.isId_p(id_p)) {// SI EXISTEN
				relPNS = listaPeliculasns
						.deleteAllPeliculaVistaNoSiFromId_P(id_p);
			} else {
				relPNS = true;
			}
			boolean relVP = false;
			if (gvp.isId_p(id_p)) {
				relVP = gvp.deleteAllValoracionPeliculaFromId_P(id_p);
			} else {
				relVP = true;
			}
			boolean bPel = borrarFin(id_p);
			boolean corec = false;
			if (bPel == true) {
				if (relVP == true) {
					if (relPNS == true) {
						corec = true;
					}
				}
			}
			rea = corec;
		}
		return rea;
	}

	/**
	 * Metodo privado utilizado para la eleminacion de una pelicula una vez que
	 * esta ya no esta en niguna otra tabla de la base de datos.
	 * 
	 * @param id_p
	 *            -> Id de la pelicula que se desea eliminar
	 * @return true si se eleimina de forma correcta. False si no.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	private boolean borrarFin(int id_p) throws ClassNotFoundException {
		boolean rea = false;

		objConversor.connect();
		objConversor.deletePelicula(id_p);
		rea = true;
		objConversor.disconnect();

		return rea;
	}

	/**
	 * Nos indica si hay peliculas almacenadas en el sistema o no.
	 * 
	 * @return true cuando no exista ninguna pelicula en el sistema.False cuando
	 *         si.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public boolean estaVacia() throws ClassNotFoundException {

		cargarDatos();
		return listaPeliculas.isEmpty();
	}

	/**
	 * Obtener todos los titulos de las peliculas almacenadas en la base de
	 * datos.
	 * 
	 * @return Un Array de String con todos los titulos.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getTitulo() throws ClassNotFoundException {

		cargarDatos();
		String[] titulos = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			titulos[i] = pel.getTitulo();
			i++;
		}
		return titulos;

	}

	/**
	 * Obtiene todas los titulos de las peliculas en las que el director sea el
	 * indicado.
	 * 
	 * @param director
	 *            -> Director del cual se desea sacar los titulos de las
	 *            peliculas
	 * @return Un Array de String con todos los titulos que cumplen ser del
	 *         director.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getTitulos(String director) throws ClassNotFoundException {

		cargarDatos();
		String[] titulos = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			String titulo = pel.getTitulo();
			boolean esta = false;
			for (int y = 0; y < i; y++) {
				if (titulos[y].equals(titulo)) {
					esta = true;
					y = i;
				}
			}
			if (esta == false) {
				titulos[i] = pel.getTitulo();
				i++;
			}
		}
		String[] fin = new String[i];

		for (int x = 0; x < i; x++) {
			fin[x] = titulos[x];
		}
		return fin;
	}

	/**
	 * Recoge todos los diferentes paises que esten en la base de datos en un
	 * Array de String
	 * 
	 * @return Array de String con paises.
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getPaises() throws ClassNotFoundException {
		cargarDatos();
		String[] paises = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			String pais = pel.getPais();
			boolean esta = false;
			for (int y = 0; y < i; y++) {
				if (paises[y].equals(pais)) {
					esta = true;
					y = i;
				}
			}
			if (esta == false) {
				paises[i] = pel.getPais();
				i++;
			}
		}
		String[] fin = new String[i];
		for (int x = 0; x < i; x++) {
			fin[x] = paises[x];
		}
		return fin;
	}

	/**
	 * Devuelve un Array de String con todos los directores que hay en la base
	 * de datos.
	 * 
	 * @return Devuelbe un Array de String con los Directores
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getDirectores() throws ClassNotFoundException {
		cargarDatos();
		String[] Directores = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			String dir = pel.getDirector();
			boolean esta = false;
			for (int y = 0; y < i; y++) {
				if (Directores[y].equals(dir)) {
					esta = true;
					y = i;
				}
			}
			if (esta == false) {
				Directores[i] = pel.getDirector();
				i++;
			}
		}
		String[] fin = new String[i];
		for (int x = 0; x < i; x++) {
			fin[x] = Directores[x];
		}
		return fin;
	}

	/**
	 * Crea un array de String de todos los directores que hay han realizado una
	 * pelicula con el titulo indicado
	 * 
	 * @param titulo
	 *            -> Titulo de las peliculas que buscamos su director
	 * @return Devuelve un Array de String con los directores
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getDirectores(String titulo) throws ClassNotFoundException {

		cargarDatos();
		String[] Directores = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			String dir = pel.getDirector();
			boolean esta = false;
			for (int y = 0; y < i; y++) {
				if (Directores[y].equals(dir)) {
					esta = true;
					y = i;
				}
			}
			if (esta == false) {
				if (pel.getTitulo().equals(titulo)) {
					Directores[i] = pel.getDirector();
					i++;
				}
			}
		}
		String[] fin = new String[i];
		for (int x = 0; x < i; x++) {
			fin[x] = Directores[x];
		}
		return fin;
	}

	/**
	 * Crea un array de String de todos los actores que hay almacenados
	 * 
	 * @return Un Array de String con los nombres de los actores
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public String[] getActorPrinc() throws ClassNotFoundException {

		cargarDatos();
		String[] Actores = new String[listaPeliculas.size()];
		int i = 0;
		for (Pelicula pel : listaPeliculas) {
			String act = pel.getActorPrincipal();
			boolean esta = false;
			for (int y = 0; y < i; y++) {
				if (Actores[y].equals(act)) {
					esta = true;
					y = i;
				}
			}
			if (esta == false) {
				Actores[i] = pel.getActorPrincipal();
				i++;
			}
		}
		String[] fin = new String[i];
		for (int x = 0; x < i; x++) {
			fin[x] = Actores[x];
		}
		return fin;

	}

	/**
	 * Devuelve la lista de peliculas almacenadas en la base de datos
	 * 
	 * @return Devuelve las peliculas en un HashSet
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public HashSet<Pelicula> getPelis() throws ClassNotFoundException {

		cargarDatos();
		return listaPeliculas;
	}

	/**
	 * Obtener el id de la pelicula que tenga el titulo y director pasado. Una
	 * pelicula tiene una combinacion de titulo y director unica
	 * 
	 * @param titulo
	 *            -> Titulo de la pelicula
	 * @param director
	 *            -> Director de la pelicula
	 * @return Devuelve el id de la pelicula
	 * @throws FaltanDatosEnLaBaseDeDatos
	 *             Cuando no existe una pelicula con ese titulo y de ese
	 *             director
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public int getId(String titulo, String director)
			throws FaltanDatosEnLaBaseDeDatos, ClassNotFoundException {
		// Las dos condiones que hacen que una pelicula sea unica.

		int id = 0;
		cargarDatos();
		for (Pelicula p : listaPeliculas) {

			if (p.getTitulo().equals(titulo)) {
				if (p.getDirector().equals(director)) {
					id = p.getId_p();
				}
			}

		}
		if (id == 0) {
			FaltanDatosEnLaBaseDeDatos f = new FaltanDatosEnLaBaseDeDatos();
			f.setFalta("No existe en la base de datos esta pelicula con el titulo->"
					+ titulo + " y director-> " + director);
			throw f;
		}
		return id;
	}

	/**
	 * Obtner la Pelicula (Obj) dado su id
	 * 
	 * @param id
	 *            -> Id de la pelicula
	 * @return Pelicula (Obj)
	 * @throws FaltanDatosEnLaBaseDeDatos
	 *             Se produce cuando no existe ese id en la base de datos
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public Pelicula getPelicula(int id) throws FaltanDatosEnLaBaseDeDatos,
			ClassNotFoundException {

		cargarDatos();
		Pelicula dev = null;

		for (Pelicula p : listaPeliculas) {
			if (p.getId_p() == id) {
				dev = p;
			}
		}

		if (dev == null) {
			FaltanDatosEnLaBaseDeDatos f = new FaltanDatosEnLaBaseDeDatos();
			f.setFalta("No existe en la base de datos esta pelicula con el id->"
					+ id);
			throw f;
		}

		return dev;
	}

	/**
	 * Nos devuelve las peliculas de un pais
	 * 
	 * @param pais
	 *            -> Pais del que queremos saber las peliculas
	 * @return ArrayList de las peliculas
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public ArrayList<Pelicula> busPais(String pais)
			throws ClassNotFoundException {
		cargarDatos();
		ArrayList<Pelicula> encontrados = new ArrayList<Pelicula>();
		for (Pelicula peli : listaPeliculas) {
			if (peli.getPais().equals(pais)) {
				encontrados.add(peli);
			}
		}
		return encontrados;
	}

	/**
	 * Nos devuelve las peliculas de un director
	 * 
	 * @param dire
	 *            -> Director del que queremos sabar las peliculas
	 * @return ArrayList de las peliculas
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public ArrayList<Pelicula> busDirec(String dire)
			throws ClassNotFoundException {
		cargarDatos();
		ArrayList<Pelicula> encontrados = new ArrayList<Pelicula>();
		for (Pelicula peli : listaPeliculas) {
			if (peli.getDirector().equals(dire)) {
				encontrados.add(peli);
			}
		}
		return encontrados;
	}

	/**
	 * Nos devulve las peliculas de un actor principal
	 * 
	 * @param actor
	 *            -> Actor del que queremos saber sus peliculas
	 * @return ArrayList de las peliculas
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public ArrayList<Pelicula> busActor(String actor)
			throws ClassNotFoundException {
		cargarDatos();
		ArrayList<Pelicula> encontrados = new ArrayList<Pelicula>();
		for (Pelicula peli : listaPeliculas) {
			if (peli.getActorPrincipal().equals(actor)) {
				encontrados.add(peli);
			}
		}
		return encontrados;
	}

	/**
	 * Borra todas las peliculas y comprueba que sea asi
	 * 
	 * @return True si se ha borrado todas las peliculas con existo
	 * @throws ClassNotFoundException
	 */
	public boolean reset() throws ClassNotFoundException {

		cargarDatos();
		ExcepDeleteOnCascade e = new ExcepDeleteOnCascade();
		e.setAceptado(true);
		for (Pelicula p : listaPeliculas) {
			try {
				borrar(p.getId_p(), e);
			} catch (ExcepDeleteOnCascade e1) {
			}
		}
		cargarDatos();
		boolean dev = false;
		if (listaPeliculas.isEmpty()) {
			dev = true;
		}
		return dev;
	}

	/**
	 * Nos indica si existe una pelicula con un id
	 * 
	 * @param id_p
	 *            --> De la pelicula que queremos saber si existe
	 * @return Devuelve un true si existe una pelicula con ese id
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public boolean isId(int id_p) throws ClassNotFoundException {
		boolean esta = false;
		HashSet<Integer> ids;
		objConversor.connect();
		ids = objConversor.listarPeliculasSoloId();
		objConversor.disconnect();

		for (Integer inte : ids) {
			if (inte.intValue() == id_p) {
				esta = true;
			}
		}
		return esta;
	}

	/**
	 * Nos indica si esta la Pelicula o no
	 * 
	 * @param p
	 *            ->Un Obj pelicula
	 * @return Devuelve true si esta la Pelicula y false si no
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public boolean anadido(Pelicula p) throws ClassNotFoundException {
		cargarDatos();
		boolean con = false;
		for (Pelicula pel : listaPeliculas) {
			if (pel.getTitulo().equals(p.getTitulo())) {
				if (pel.getDirector().equals(p.getDirector())) {
					con = true;
				}
			}
		}
		return con;
	}

	/**
	 * Nos devuelve una pelicula concreta
	 * 
	 * @param titulo
	 *            -> Titulo de la pelicula
	 * @param director
	 *            -> Director de la pelicula
	 * @return Devuelve la pelicula, que tiene como titulo y director el pasado.
	 *         Solo hay una pelicula con ese titulo y director
	 * @throws FaltanDatosEnLaBaseDeDatos
	 *             Si no hay pelicula que tenga ese titulo y director
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public Pelicula getPelicula(String titulo, String director)
			throws FaltanDatosEnLaBaseDeDatos, ClassNotFoundException {
		cargarDatos();
		Pelicula pel = null;
		for (Pelicula p : listaPeliculas) {
			if (p.getTitulo().equals(titulo)) {
				if (p.getDirector().equals(director)) {
					pel = p;
				}
			}
		}
		if (pel == null) {
			FaltanDatosEnLaBaseDeDatos f = new FaltanDatosEnLaBaseDeDatos();
			f.setFalta("No existe una pelicula en la base de datos con titulo: "
					+ titulo + " y director: " + director);
			throw f;
		}
		return pel;
	}

	/**
	 * Devuelve todas las peliculas que estan en la lista
	 * 
	 * @param pelisUsu
	 *            Lista de peliculas,Solo id_p, que su id esta en la lista
	 *            pasada
	 * @return Peliculas (obj) que el usuario no ha visto
	 * @throws ClassNotFoundException
	 *             Se produce cuando no existe la base de datos
	 */
	public HashSet<Pelicula> getPelis(HashSet<ValorId_p> pelisUsu)
			throws ClassNotFoundException {

		HashSet<Pelicula> peliculas = new HashSet<Pelicula>();

		objConversor.connect();
		for (ValorId_p pvns : pelisUsu) {

			Pelicula p = objConversor.getPelicula(pvns.getId_p());
			if (p != null) {
				peliculas.add(p);
			}
		}
		objConversor.disconnect();

		return peliculas;
	}
}